# Лекция: Многопоточное программирование. Thread, synchronized в Java

Многопоточное программирование в Java позволяет выполнять несколько операций одновременно в отдельных потоках.

## Класс Thread

Потоки в Java представлены классом `Thread`. Новый поток можно создать, создав экземпляр класса `Thread` и переопределив метод `run()`.

```java
Thread thread = new Thread() {
    @Override
    public void run() {
        // код, который будет выполняться в потоке
    }
};
```

Запуск потока осуществляется с помощью метода `start()`.

```java
thread.start();
```

## Синхронизация

Синхронизация в Java используется для контроля доступа к общим ресурсам в многопоточной среде. Она осуществляется с помощью ключевого слова `synchronized`.

```java
synchronized (object) {
    // критический участок кода
}
```

---

# Практические задания

1. **Создайте поток, который выводит "Hello, world!" и запустите его.** (Сложность: 1/5)
```java
Thread thread = new Thread() {
    @Override
    public void run() {
        System.out.println("Hello, world!");
    }
};
thread.start();
```

2. **Создайте два потока, каждый из которых выводит числа от 1 до 10, и запустите их.** (Сложность: 2/5)
```java
Runnable runnable = () -> {
    for (int i = 1; i <= 10; i++) {
        System.out.println(i);
    }
};
new Thread(runnable).start();
new Thread(runnable).start();
```

3. **Создайте класс `Counter` с полем `value` и методами `increment` и `getValue`. Сделайте метод `increment` синхронизированным. Создайте два потока, которые увеличивают значение счетчика, и запустите их.** (Сложность: 3/5)
```java
class Counter {
    private int value = 0;

    synchronized void increment() {
        value++;
    }

    int getValue() {
        return value;
    }
}

Counter counter = new Counter();
Runnable runnable = () -> {
    for (int i = 0; i < 1000; i++) {
        counter.increment();
    }
};
Thread thread1 = new Thread(runnable);
Thread thread2 = new Thread(runnable);
thread1.start();
thread2.start();
// дождитесь завершения потоков
thread1.join();
thread2.join();
System.out.println(counter.getValue()); // выводит 2000
```

4. **Создайте класс `BankAccount` с полем `balance` и методами `deposit`, `withdraw` и `getBalance`. Сделайте методы `deposit` и `withdraw` синхронизированными. Создайте два потока: один делает вклады, а другой - снятие денег, и запустите их.** (Сложность: 4/5)
```java
class BankAccount {
    private int balance = 0;

    synchronized void deposit(int amount) {
        balance += amount;
    }

    synchronized void withdraw(int amount) {
        balance -= amount;
    }

    int getBalance() {
        return balance;
    }
}

BankAccount account = new BankAccount();
Thread thread1 = new Thread(() -> {
    for (int i = 0; i < 1000; i++) {
        account.deposit(1);
    }
});
Thread thread2 = new Thread(() -> {
    for (int i = 0; i < 1000; i++) {
        account.withdraw(1);
    }
});
thread1.start();
thread2.start();
// дождитесь завершения потоков
thread1.join();
thread2.join();
System.out.println(account.getBalance()); // выводит 0
```

5. **Создайте класс `Printer`, который выводит сообщения в консоль. Сделайте метод `print` синхронизированным. Создайте два потока, которые печатают сообщения, и запустите их.** (Сложность: 4/5)
```java
class Printer {
    synchronized void print(String message) {
        System.out.println(message);
    }
}

Printer printer = new Printer();
Thread thread1 = new Thread(() -> {
    for (int i = 0; i < 1000; i++) {
        printer.print("Hello from Thread 1!");
    }
});
Thread thread2 = new Thread(() -> {
    for (int i = 0; i < 1000; i++) {
        printer.print("Hello from Thread 2!");
    }
});
thread1.start();
thread2.start();
```